﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace AMOOF {
    public delegate void DelegateLoadPDFPanel(Panel pdf_label, int index);
    public delegate void DelegateSetToolTip(int panelIndex, int sovlerIndex);
    public delegate void DelegateUpdateHistogram(Bitmap bmp);

    public partial class OptimizeModels : UserControl {

        private Dictionary<int, string> solverSolveValues = new Dictionary<int, string>();

        private List<Point> list = new List<Point>();

        private Thread m_SolverThread;
        private Thread m_displayHistogramThread;
        private DelegateLoadPDFPanel m_delegateLoadPDFPanel;
        private DelegateSetToolTip m_delegateSetToolTip;
        private DelegateUpdateHistogram m_delegateUpdateHistogram;
        private bool loaded = false;
        private string pdf_key;
        private Dictionary<int, Panel> pdf_displays = new Dictionary<int, Panel>();
        
        public OptimizeModels() {
            InitializeComponent();

            btnMarkForRerun.Text = "\u2713 " + "Mark for Rerun";

            solverSolveValues.Add(0,"Solver found a solution. All constraints and optimality conditions are satisfied.");
            solverSolveValues.Add(1,"Solver has converged to the current solution. All constraints are satisfied.");
            solverSolveValues.Add(2,"Solver cannot improve the current solution. All constraints are satisfied.");
            solverSolveValues.Add(3,"Stop chosen when the maximum iteration limit was reached.");
            solverSolveValues.Add(4,"The Objective Cell values do not converge.");
            solverSolveValues.Add(5,"Solver could not find a feasible solution.");
            solverSolveValues.Add(6,"Solver stopped at user’s request.");
            solverSolveValues.Add(7,"The linearity conditions required by this LP Solver are not satisfied.");
            solverSolveValues.Add(8,"The problem is too large for Solver to handle.");
            solverSolveValues.Add(9,"Solver encountered an error value in a target or constraint cell.");
            solverSolveValues.Add(10,"Stop chosen when the maximum time limit was reached.");
            solverSolveValues.Add(11,"There is not enough memory available to solve the problem.");
            solverSolveValues.Add(12, "The Likelihood functions contain an error with the current parameters.");
            solverSolveValues.Add(13,"Error in model. Please verify that all cells and constraints are valid.");
            solverSolveValues.Add(14,"Solver found an integer solution within tolerance. All constraints are satisfied.");
            solverSolveValues.Add(15,"Stop chosen when the maximum number of feasible [integer] solutions was reached.");
            solverSolveValues.Add(16,"Stop chosen when the maximum number of feasible [integer] subproblems was reached.");
            solverSolveValues.Add(17,"Solver converged in probability to a global solution.");
            solverSolveValues.Add(18,"All variables must have both upper and lower bounds.");
            solverSolveValues.Add(19,"Variable bounds conflict in binary or alldifferent constraint.");
            solverSolveValues.Add(20,"Lower and upper bounds on variables allow no feasible solution.");
            solverSolveValues.Add(21, "The moments cannot be calculated with the current parameters.");
            solverSolveValues.Add(22, "Solver failed to run. Please select better starting parameters.");
            m_delegateLoadPDFPanel = new DelegateLoadPDFPanel(LoadPDFPanel);
            m_delegateSetToolTip = new DelegateSetToolTip(SetToolTip);
            m_delegateUpdateHistogram = new DelegateUpdateHistogram(UpdateHistogram);
            btnRunCalculations.Text = "Run Excel Solver";

            for (int i = 1; i < 11; i++) {
                ((NumericUpDown)pnlSolverResultsContainer.Controls["nudGreek" + i]).ValueChanged += new EventHandler(Greek_nud_ValueChanged);
                
            }
        }

        private void Greek_nud_ValueChanged(object sender, EventArgs e) {
            if (((Control)sender).Tag == "Flagged") {
                ((Control)sender).Tag = "";
                return;
            }
            if (((Control)sender).Visible) {
                Report r = FormAMOOF.Instance.GetReport(pdf_key);
                int index = int.Parse(((Control)sender).Name.Substring(8));
                int startLocation = ((Control)sender).Name.Length - 1;
                Control obj = pnlSolverResultsContainer.Controls["lblGreek" + ((Control)sender).Name.Substring(startLocation) + "Title"];
                string str = obj.Text.Substring(0, obj.Text.Length - 2);
                PDF.Greek greek = Reflector.GetEnum<PDF.Greek>(str);
                
                // modified this to make code more readable.
                NumericUpDown modifiedControl = (NumericUpDown)sender;
                if (greek.Equals(PDF.Greek.P1) || greek.Equals(PDF.Greek.P2)) {
                    PDF.Greek otherGreek = greek.Equals(PDF.Greek.P1) ? PDF.Greek.P2 : PDF.Greek.P1;
                    NumericUpDown otherControl = greek.Equals(PDF.Greek.P1) ?
                        ((NumericUpDown)pnlSolverResultsContainer.Controls["nudGreek" + (index + 1)]) :
                        ((NumericUpDown)pnlSolverResultsContainer.Controls["nudGreek" + (index - 1)]);
                    otherControl.Tag = "Flagged";
                    if ((double)modifiedControl.Value > 1) {
                        modifiedControl.Value = (decimal)1.0;
                        otherControl.Value = (decimal)0.0;
                    }
                    else {
                        otherControl.Value = (decimal)(1 - (double)modifiedControl.Value);
                        otherControl.Text = (otherControl.Value).ToString();
                    }
                    r.PDF.Greeks[otherGreek] = (double)otherControl.Value;
                }
                r.PDF.Greeks[greek] = (double)modifiedControl.Value;

                // run solver
                Solver solver = FormAMOOF.Instance.GetSolver();
                solver.SetSolverParams(ref r);
                for (int i = 1; i < 5; i++) {
                    pnlSolverResultsContainer.Controls["lblMoment" + i].Text = r.PDF.Moments[i].ToString();
                    pnlSolverResultsContainer.Controls["lblMoment_" + i].Text = r.PDF.Moments[-i].ToString();
                }
                lblAverageErrorValue.Text = r.PDF.AverageError.ToString();
                Histogram histo = FormAMOOF.Instance.GetHistogram();
                histo.ClearPDFs();
                histo.AddPDF(FormAMOOF.Instance.GetReport(pdf_key).PDF, Color.ForestGreen);
                m_displayHistogramThread = new Thread(this.DisplayHistogram);
                m_displayHistogramThread.Start(histo);
            }
        }

        //private void Greek_nud_ValueChanged(object sender, EventArgs e) {
        //    if (((Control)sender).Tag == "Flagged") {
        //        ((Control)sender).Tag = "";
        //        return;
        //    }
        //    if (((Control)sender).Visible) {
        //        Report r = FormAMOOF.Instance.GetReport(pdf_key);
        //        int nudVal = int.Parse(((Control)sender).Name.Substring(8));
        //        int startLocation = ((Control)sender).Name.Length - 1;
        //        Control obj = pnlSolverResultsContainer.Controls["lblGreek" + ((Control)sender).Name.Substring(startLocation) + "Title"];
        //        string str = obj.Text.Substring(0, obj.Text.Length - 2);
        //        PDF.Greek greek = Reflector.GetEnum<PDF.Greek>(str);
        //        object o;
        //        if (greek.Equals(PDF.Greek.P1)) {
        //            ((NumericUpDown)pnlSolverResultsContainer.Controls["nudGreek" + (nudVal + 1)]).Tag = "Flagged";
        //            o = ((NumericUpDown)pnlSolverResultsContainer.Controls["nudGreek" + (nudVal + 1)]).Name;
        //            if ((double)((NumericUpDown)sender).Value > 1) {
        //                ((NumericUpDown)sender).Value = (decimal)1.0;
        //                ((NumericUpDown)pnlSolverResultsContainer.Controls["nudGreek" + (nudVal + 1)]).Value = (decimal)0.0;
        //            }
        //            else {
        //                ((NumericUpDown)pnlSolverResultsContainer.Controls["nudGreek" + (nudVal + 1)]).Value = (decimal)(1 - (double)((NumericUpDown)sender).Value);
        //                ((NumericUpDown)pnlSolverResultsContainer.Controls["nudGreek" + (nudVal + 1)]).Text = (1 - (double)((NumericUpDown)sender).Value).ToString();
        //            }
        //            r.PDF.Greeks[PDF.Greek.P2] = (double)((NumericUpDown)pnlSolverResultsContainer.Controls["nudGreek" + (startLocation + 1)]).Value;
        //        }
        //        else if (greek.Equals(PDF.Greek.P2)) {
        //            ((NumericUpDown)pnlSolverResultsContainer.Controls["nudGreek" + (nudVal - 1)]).Tag = "Flagged";
        //            o = ((NumericUpDown)pnlSolverResultsContainer.Controls["nudGreek" + (nudVal - 1)]).Name;
        //            if ((double)((NumericUpDown)sender).Value > 1) {
        //                ((NumericUpDown)sender).Value = (decimal)1.0;
        //                ((NumericUpDown)pnlSolverResultsContainer.Controls["nudGreek" + (nudVal - 1)]).Value = (decimal)0.0;
                        
        //            }
        //            else {
        //                ((NumericUpDown)pnlSolverResultsContainer.Controls["nudGreek" + (nudVal - 1)]).Value = (decimal)(1 - (double)((NumericUpDown)sender).Value);
        //                ((NumericUpDown)pnlSolverResultsContainer.Controls["nudGreek" + (nudVal - 1)]).Text = (1 - (double)((NumericUpDown)sender).Value).ToString();
        //            }
        //            r.PDF.Greeks[PDF.Greek.P1] = (double)((NumericUpDown)pnlSolverResultsContainer.Controls["nudGreek" + (startLocation - 1)]).Value;
        //        }

        //        r.PDF.Greeks[greek] = (double)((NumericUpDown)sender).Value;

        //        Solver solver = FormAMOOF.Instance.GetSolver();
        //        solver.SetSolverParams(ref r);

        //        for (int i = 1; i < 5; i++) {
        //            pnlSolverResultsContainer.Controls["lblMoment" + i].Text = r.PDF.Moments[i].ToString();
        //            pnlSolverResultsContainer.Controls["lblMoment_" + i].Text = r.PDF.Moments[-i].ToString();
        //        }

        //        lblAverageErrorValue.Text = r.PDF.AverageError.ToString();

        //        Histogram histo = FormAMOOF.Instance.GetHistogram();
        //        histo.ClearPDFs();
        //        histo.AddPDF(FormAMOOF.Instance.GetReport(pdf_key).PDF, Color.ForestGreen);
        //        m_displayHistogramThread = new Thread(this.DisplayHistogram);
        //        m_displayHistogramThread.Start(histo);
        //    }
        //}

        private void UpdateHistogram(Bitmap bmp) {
            if (pnlDiscreteHistogram.InvokeRequired) {
                m_delegateUpdateHistogram = new DelegateUpdateHistogram(UpdateHistogram);
                this.Invoke(m_delegateUpdateHistogram, new object[] { bmp });
            }
            else {
                pnlDiscreteHistogram.BackgroundImage = bmp;
            }
        }

        private void SetToolTip(int panelIndex, int solverIndex) {
            if (this.InvokeRequired) {
                m_delegateSetToolTip = new DelegateSetToolTip(SetToolTip);
                this.Invoke(m_delegateSetToolTip, new object[] {panelIndex, solverIndex});
            }
            else {
                ToolTip tt = new ToolTip();
                tt.SetToolTip(pdf_displays[panelIndex].Controls["btn" + pdf_displays[panelIndex].Name.Substring(3)], solverSolveValues[solverIndex]);
            }
        }

        private void btnRunCalculations_Click(object sender, EventArgs e) {
            m_SolverThread = new Thread(RunCalculations);
            m_SolverThread.Start();
        }

        private void LoadPDFPanel(Panel pdf_panel, int index) {
            if (this.InvokeRequired) {
                m_delegateLoadPDFPanel = new DelegateLoadPDFPanel(LoadPDFPanel);
                this.Invoke(m_delegateLoadPDFPanel, new object[] { pdf_panel, index });
            }
            else {
                gbxPDFResults.Visible = loaded;


                list.Add(pdf_panel.Location);
                pdf_displays[index] = pdf_panel;


                pnlPDFResults.Invalidate();
                pnlPDFResults.Update();

                if (gbxPDFSolverResults.Visible) {
                    gbxPDFSolverResults.Invalidate();
                    gbxPDFSolverResults.Update();
                }
                
            }
        }

        [STAThread]
        private void RunCalculations() {
            Dictionary<string, Report> reports = FormAMOOF.Instance.GetReports();
            Solver solver = FormAMOOF.Instance.GetSolver();
            string statement1 = "Running Solver... ";

            double counter = 0;
            double percent = counter / reports.Count;

            FormAMOOF.Instance.SetStatus(string.Format("{0}{1:00.00}% complete. Go enjoy some tea while this runs!", statement1, percent));
            FormAMOOF.Instance.SetProgress((int)percent);


            foreach (var r in reports) {

                percent = (counter / reports.Count) * 100;

                statement1 = "Running Solver: Optimizing " + r.Value.PDF.name;

                FormAMOOF.Instance.SetStatus(string.Format("{0} Overall Completion: {1:00.00}%. Go enjoy some tea while this runs!", statement1, percent));
                FormAMOOF.Instance.SetProgress((int)percent);
                
                if (r.Value.Primed) {
                    solver.RunSolver(r.Value);
                    LoadPDFPanel(updatePDFPanel(r.Value, (int)counter), (int)counter);
                    r.Value.Primed = false;
                }

                counter++;
                

            }

            FormAMOOF.Instance.LoadSolver(solver);
            FormAMOOF.Instance.LoadReports(reports);

            statement1 = "Solver complete! Please review the results and rerun if necessary.";
            FormAMOOF.Instance.SetStatus(string.Format("{0}", statement1));
            FormAMOOF.Instance.SetProgress(-1);
        }



        private Panel updatePDFPanel(Report r, int offset) {
            
            string color = "";
            string mouseOverText = solverSolveValues[r.SolverResult];


            if (r.SolverResult < 3) {
                color = "green";
            }
            else if (r.SolverResult == 3) {
                color = "yellow";
            }
            else if (r.SolverResult < 12) {
                color = "red";
            }
            else if (r.SolverResult == 12) {
                color = "red";
            }
            else if (r.SolverResult < 17) {
                color = "yellow";
            }
            else if (r.SolverResult == 17) {
                color = "yellow";
            }
            else {
                color = "red";
            }
            
            string button = String.Format("btn_round_{0}_",color);


            SetToolTip(offset, r.SolverResult);

            ((OnOffButton)pdf_displays[offset].Controls["btn" + pdf_displays[offset].Name.Substring(3)]).OnImage = button + "on";
            ((OnOffButton)pdf_displays[offset].Controls["btn" + pdf_displays[offset].Name.Substring(3)]).OffImage = button + "off";

            ((OnOffButton)pdf_displays[offset].Controls["btn" + pdf_displays[offset].Name.Substring(3)]).Selected = ((OnOffButton)pdf_displays[offset].Controls["btn" + pdf_displays[offset].Name.Substring(3)]).Selected;

            return pdf_displays[offset];
        }

        private void GeneratePDFs() {
            var reports = FormAMOOF.Instance.GetReports();
            int offset = 0;

            foreach (var kvp in reports) {
                Panel p = new Panel();
                OnOffButton btn = new OnOffButton();
                Label pdf = new Label();

                string name = kvp.Value.IsMixed ? "Mixed" + kvp.Value.PDF.name.Replace(" ", "")  : kvp.Value.PDF.name.Replace(" ", "");

                p.Name = "pnl" + name;
                btn.Name = "btn" + name;
                pdf.Name = "lbl" + name;

                btn.BackgroundImage = (Bitmap)Properties.Resources.ResourceManager.GetObject("btn_round_blue_off");

                btn.OnImage = "btn_round_blue_on";
                btn.OffImage = "btn_round_blue_off";

                btn.Location = new Point(0, 3);
                btn.Size = new Size(28, 28);

                pdf.Size = new Size(254, 18);
                pdf.Location = new Point(28, 8);
                pdf.Font = new Font("Arial", 12, FontStyle.Regular);
                pdf.Text = kvp.Value.IsMixed ? "Mixed " + kvp.Value.PDF.name :kvp.Value.PDF.name;

                p.Tag = offset;

                btn.MouseClick += new MouseEventHandler(PDF_Selected);
                pdf.MouseClick += new MouseEventHandler(PDF_Selected);
                p.MouseClick += new MouseEventHandler(PDF_Selected);

                p.Controls.Add(btn);
                p.Controls.Add(pdf);
                p.Size = new Size(303, 34);
                p.Location = new Point(0, p.Height * offset);
                pdf_displays.Add(offset, p);
                pnlPDFResults.Controls.Add(p);
                offset++;
            }



        }

        private void OptimizeModels_Load(object sender, EventArgs e) {
            gbxPDFResults.Visible = loaded;
            gbxPDFSolverResults.Visible = loaded;
            GeneratePDFs();
            loaded = true;
        }

        private void btnMarkForRerun_Click(object sender, EventArgs e) {
            var reports = FormAMOOF.Instance.GetReports();
            btnMarkForRerun.Active = btnMarkForRerun.Active ? false : true;
            btnMarkForRerun.Text = btnMarkForRerun.Active ? "\u2713 " + "Mark for Rerun" : "Mark for Rerun";
            foreach (var kvp in pdf_displays) {
                if (((OnOffButton)pdf_displays[kvp.Key].Controls["btn" + pdf_displays[kvp.Key].Name.Substring(3)]).Selected) {
                    reports[kvp.Value.Name.Substring(3)].Primed = btnMarkForRerun.Active;
                }
            }

            FormAMOOF.Instance.LoadReports(reports);
        }

        private void PDF_Selected(object sender, EventArgs e) {
            gbxPDFSolverResults.Visible = loaded;


            foreach (var kvp in pdf_displays) {
                if (((OnOffButton)pdf_displays[kvp.Key].Controls["btn" + pdf_displays[kvp.Key].Name.Substring(3)]).Selected) {
                    ((OnOffButton)pdf_displays[kvp.Key].Controls["btn" + pdf_displays[kvp.Key].Name.Substring(3)]).Selected = false;
                    ((Label)pdf_displays[kvp.Key].Controls["lbl" + pdf_displays[kvp.Key].Name.Substring(3)]).ForeColor = Color.Black;
                    
                }
            }

            string reportKey = "";

            if (sender is OnOffButton) {
                ((OnOffButton)sender).Selected = true;
                reportKey = ((Control)sender).Parent.Name.Substring(3);
                ((Control)sender).Parent.Controls["lbl" + reportKey].ForeColor = Color.White;

                
            }
            else if (sender is Label) {
                int index = (int)((Control)sender).Parent.Tag;
                ((OnOffButton)pdf_displays[index].Controls["btn" + pdf_displays[index].Name.Substring(3)]).Selected = true;
                reportKey = ((Control)sender).Parent.Name.Substring(3);
                ((Control)sender).Parent.Controls["lbl" + reportKey].ForeColor = Color.White;
            }
            else if (sender is Panel) {
                ((OnOffButton)pdf_displays[(int)((Control)sender).Tag].Controls["btn" + pdf_displays[(int)((Control)sender).Tag].Name.Substring(3)]).Selected = true;
                reportKey = ((Control)sender).Name.Substring(3);
                ((Control)sender).Controls["lbl" + reportKey].ForeColor = Color.White;
            }

            LoadSolverResultsFromReport(reportKey);

            Histogram histo = FormAMOOF.Instance.GetHistogram();
            histo.ClearPDFs();
            histo.AddPDF(FormAMOOF.Instance.GetReport(reportKey).PDF, Color.ForestGreen);
            m_displayHistogramThread = new Thread(this.DisplayHistogram);
            m_displayHistogramThread.Start(histo);

        }

        private void DisplayHistogram(object obj) {
            Histogram hist = (Histogram)obj;
            
            this.UpdateHistogram(hist.GetHistogramBitmap(pnlDiscreteHistogram.Size, FormAMOOF.Instance.GetReport(pdf_key).PDF));

            FormAMOOF.Instance.LoadHistogram(hist);
        }

        public void LoadSolverResultsFromReport(string reportKey) {
            Report r = FormAMOOF.Instance.GetReport(reportKey);
            this.pdf_key = reportKey;


            lblPDFName.Text = r.IsMixed ? "Mixed " + r.PDF.name : r.PDF.name;

            if (r.Solved) {
                btnMarkForRerun.Active = r.Primed;
                btnMarkForRerun.Text = r.Primed ? "\u2713 " + "Mark for Rerun" : "Mark for Rerun";
                pnlSolverResultsContainer.Visible = true;
                
                btnMarkForRerun.Enabled = true;
                for (int i = 1; i < 11; i++) {
                    pnlSolverResultsContainer.Controls["nudGreek" + i].Visible = false;
                    pnlSolverResultsContainer.Controls["lblGreek" + i + "Title"].Visible = false;
                    ((NumericUpDown)pnlSolverResultsContainer.Controls["nudGreek" + i]).Value = 0;
                    pnlSolverResultsContainer.Controls["lblGreek" + i + "Title"].Text = "Greek" + i;
                }

                for (int i = 1; i < 5; i++) {
                    object o = r.PDF.Moments[i];
                    pnlSolverResultsContainer.Controls["lblMoment" + i].Text = !r.PDF.Moments[i].Equals((object)-2146826252) ? r.PDF.Moments[i].ToString() : "Does not exist";
                    pnlSolverResultsContainer.Controls["lblMoment_" + i].Text = !r.PDF.Moments[-i].Equals((object)-2146826252) ? r.PDF.Moments[-i].ToString() : "Does not exist";
                }

                lblAverageErrorValue.Text = r.PDF.AverageError.ToString();

                int counter = 1;
                
                foreach (var kvp in r.PDF.Greeks) {
                    if (kvp.Value > 260000000000000) // had to add this limit, threw an error ... Jim, 11/16/12
                    {
                        pnlSolverResultsContainer.Controls["lblGreek" + counter + "Title"].Text = kvp.Key.ToString() + ": ";
                        ((NumericUpDown)pnlSolverResultsContainer.Controls["nudGreek" + counter]).Value = (decimal)10101010;
                        ((NumericUpDown)pnlSolverResultsContainer.Controls["nudGreek" + counter]).DecimalPlaces = 0;
                        pnlSolverResultsContainer.Controls["nudGreek" + counter].Visible = true;
                        pnlSolverResultsContainer.Controls["lblGreek" + counter + "Title"].Visible = true;
                        counter++;
                        continue;
                    }
                    
                    pnlSolverResultsContainer.Controls["lblGreek" + counter + "Title"].Text = kvp.Key.ToString() + ": ";
                    ((NumericUpDown)pnlSolverResultsContainer.Controls["nudGreek" + counter]).Value = (decimal)kvp.Value;
                    ((NumericUpDown)pnlSolverResultsContainer.Controls["nudGreek" + counter]).DecimalPlaces = kvp.Value.ToString().Contains('.') ? kvp.Value.ToString().Split('.')[1].Length: 0;
                    pnlSolverResultsContainer.Controls["nudGreek" + counter].Visible = true;
                    pnlSolverResultsContainer.Controls["lblGreek" + counter + "Title"].Visible = true;
                    counter++;
                }


                
            }
            else {
                pnlSolverResultsContainer.Visible = false;
                
                btnMarkForRerun.Enabled = false;
            }


        }
    }
}
